python __new__について
__new__というメソッドについて知り、より可読性の高いpythonコードを書けるようになる!!
__new__の仕様
インスタンスの初期化前に呼び出されるクラスメソッド(特殊)
指定クラスのインスタンスを返すと、そのインスタンスの__init__メソッドが呼び出される
逆に言うと、返さない場合は__init__が呼び出されない
参考
切り取り線.icon
特殊メソッドの__new__は、インスタンスの初期化前に「インスタンスを生成するために」処理が実行されます。実装する際、__new__メソッドの第1引数はclsを指定しますが、クラスオブジェクトです。また、戻り値にそのクラスのインスタンスを指定した場合、そのインスタンスの___init__メソッドが呼びだされます。
__init__が呼び出されないとどうなる?
インスタンスが存在しなくなる
q.icon __init__って具体的に何してるんや?
__init__の前の段階、selfがある段階では、もうすでにインスタンスがあるんじゃ無いのか?
__new__を超えた段階で、もうインスタンスが生成されてる認識だったんだが
a.icon 初期化してる
インスタンスは初期化しないと利用できない
生成されただけでは利用できない
https://scrapbox.io/files/634848c8d63f7d001d5b75e3.png
ほおほお、なるほどonigiri.w2.icon
q.icon 関係ないけど、インスタンスが破壊される時ってどう言う時?
a.icon そのインスタンスに紐づく名前が全てdelされた時
__new__メソッドは、クラスのインスタンス生成をカスタマイズする際に定義する。
インスタンスの生成自体は、親クラスの__new__メソッドを呼び出して、cls(とその他の引数)を指定する、つまり「super().__new__(cls, ……)」とするのが典型的なやり方だ。
なるほど、__new__メソッドでは最後にsuper()....を返すのが定石なのねonigiri.w2.icon
加えて、__new__メソッドでしか行えないインスタンスの初期化も行える(後述)。
なるほど...何それonigiri.w2.icon
サンプルコード
code: sample.py
class Foo:
def __new__(cls):
print('__new__')
self = super().__new__(cls) # インスタンス生成を行う典型的なコード
self.attr = 'set in __new__' # ここでしかできない初期化処理を書いてもよい
return self # 生成したインスタンスを返す
def __init__(self, name='foo'):
print('__init__')
self.name = name # インスタンスの初期化処理
def __del__(self):
print('__del__') # インスタンスが破壊されるときに行う処理
はえ〜〜〜onigiri.w2.icon
__new__メソッドの中身がなんか感動的なんだが
selfってそう言うふうにして作られるのね
__init__メソッドでは「変更不可能なオブジェクトを初期化できない」
変更不可能なクラスのインスタンス生成処理においては、それが生成された時点で変更不可能なので、その初期化は__init__メソッドが呼び出されるよりも前に行う必要がある。
つまり、そうしたインスタンスの生成と初期化は__new__メソッドで行う。
code: sample.py
class NumberedTuple(tuple):
def __new__(cls, iterable):
self = super().__new__(cls, tmp)
return self
print(nt) # ((0, 'foo'), (1, 'bar'), (2, 'baz'))
なるほどなるほど、これは使い勝手がワンチャン良さそうやなonigiri.w2.icon
不変インスタンスを作る際のTipsになりそう
__new__の使うタイミング
1. immutable を初期化したい(本稿で解説)
2. singleton を実装したい(Python でシングルトンを書く)
3. 引数をもとにクラスを切り替えたいとき(メタクラスで紹介)
ほおほお
Python では、可読性でも、実装面でも immutable にしても、 あまり効果がないことを上記の記事では見てきました。
えええええええ、そうなんonigiri.w2.icon
__new__メソッドは、インスタンスを生成することを目的にしています。
__init__ メソッドは、インスタンスを初期化することを目的にしています。
生成と初期化の違いがわからんonigiri.w2.icon
a.icon そのままの意味や
pythonでは、インスタンスは生成・初期化で別々なんや
実際に immutable なクラスを定義したいときは namedtuple 関数を使います。 このページでは tuple を継承した immutable なクラスで __new__ の簡単な使い方をご紹介します。
namedtuple については、次のページでご紹介いたしますが、 namedtuple の仕組みは、ここで紹介する tuple を継承する形で immutable を実現しています。
q.icon pythonでimmutableなクラス作りたい時は、namedtupleを使うの?onigiri.w2.icon
基本構文は以下
code: sample.py
class Cls(object):
def __new__(cls):
self = super().__new__(cls)
親の__new__からインスタンスを取得してるのね。
結局元を辿れば、objectにたどり着くと
__new__は初期化されていないクラスのインスタンスを生成するのに使われる。__init__はインスタンスが生成された後に初期化のため呼び出される。 __init__によってインスタンスが生成されている訳ではないことに注意する(Pythonで__init__をコンストラクタと呼ばないのはそのためである)。 つまり、以下の2つは等価である。
code: 1.py
# 通常のインスタンス生成
obj = Class(arg)
code: 2.py
# __new__と__init__を明示的に呼び出したインスタンス生成
obj = Class.__new__(Class, arg)
obj.__init__(arg)
newした時点で存在してるものは何?wonigiri.w2.icon
a.icon インスタンス
__new__は一般的には定義されていないので、その場合は基底クラスobjectの__new__が呼び出されることになる。 つまり、以下とも等価である。
code: sample.py
# __new__が定義されていない場合のインスタンス生成
obj = object.__new__(Class, arg)
obj.__init__(arg)
なるほど、__new__がない場合は、親クラスの__new__が使われるのねonigiri.w2.icon
ほんで、全ての親で__new__が定義されてない場合は、最後の基底クラスであるobjectが利用されるのね
__new__の使い所としてはインスタンス生成のカスタマイズが考えられるだろう。 例えば、以下はシングルトン(複数のインスタンスで同一性が保証されたクラス)の実装例である。
code: sample.py
# __new__を使ったシングルトンの実装
class Singleton:
def __new__(cls):
if not hasattr(cls, "_instance"):
cls._instance = super().__new__(cls)
return cls._instance
print(Singleton() is Singleton()) # True
ほおおおおおおonigiri.w2.icon
これは完全なるシングルトンや
すげぇ。すげぇわ。